From ea8e0e534ed98de4c5e6a7815286eb37b053674a Mon Sep 17 00:00:00 2001 From: Misode Date: Sun, 31 May 2020 01:48:23 +0200 Subject: [PATCH] Move schemas to npm package --- package.json | 1 + src/Registries.ts | 103 --------- src/app/Sandbox.ts | 19 +- src/app/app.ts | 15 +- src/minecraft/nodes/RangeNode.ts | 106 --------- src/minecraft/nodes/ResourceNode.ts | 47 ---- src/minecraft/schemas/Collections.ts | 321 --------------------------- src/minecraft/schemas/Condition.ts | 97 -------- src/minecraft/schemas/LootTable.ts | 247 --------------------- src/minecraft/schemas/Predicates.ts | 132 ----------- src/model/DataModel.ts | 91 -------- src/model/Path.ts | 77 ------- src/nodes/AbstractNode.ts | 149 ------------- src/nodes/BooleanNode.ts | 37 --- src/nodes/EnumNode.ts | 47 ---- src/nodes/ListNode.ts | 63 ------ src/nodes/MapNode.ts | 69 ------ src/nodes/NumberNode.ts | 56 ----- src/nodes/ObjectNode.ts | 100 --------- src/nodes/ReferenceNode.ts | 48 ---- src/nodes/StringNode.ts | 43 ---- src/view/SourceView.ts | 51 ----- src/view/TreeView.ts | 98 -------- tsconfig.json | 1 + 24 files changed, 22 insertions(+), 1996 deletions(-) delete mode 100644 src/Registries.ts delete mode 100644 src/minecraft/nodes/RangeNode.ts delete mode 100644 src/minecraft/nodes/ResourceNode.ts delete mode 100644 src/minecraft/schemas/Collections.ts delete mode 100644 src/minecraft/schemas/Condition.ts delete mode 100644 src/minecraft/schemas/LootTable.ts delete mode 100644 src/minecraft/schemas/Predicates.ts delete mode 100644 src/model/DataModel.ts delete mode 100644 src/model/Path.ts delete mode 100644 src/nodes/AbstractNode.ts delete mode 100644 src/nodes/BooleanNode.ts delete mode 100644 src/nodes/EnumNode.ts delete mode 100644 src/nodes/ListNode.ts delete mode 100644 src/nodes/MapNode.ts delete mode 100644 src/nodes/NumberNode.ts delete mode 100644 src/nodes/ObjectNode.ts delete mode 100644 src/nodes/ReferenceNode.ts delete mode 100644 src/nodes/StringNode.ts delete mode 100644 src/view/SourceView.ts delete mode 100644 src/view/TreeView.ts diff --git a/package.json b/package.json index 6318c9cd..c9501834 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "license": "MIT", "dependencies": { "copy-webpack-plugin": "^6.0.1", + "minecraft-schemas": "^0.1.2", "ts-loader": "^7.0.4", "typescript": "^3.9.3", "webpack": "^4.43.0", diff --git a/src/Registries.ts b/src/Registries.ts deleted file mode 100644 index 4d6eedf0..00000000 --- a/src/Registries.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { INode } from "./nodes/AbstractNode" -import { Path, PathElement } from "./model/Path" - -export interface Registry { - register(id: string, value: T): void - get(id: string): T -} - -/** - * Registry for schemas - */ -class SchemaRegistry implements Registry> { - private registry: { [id: string]: INode } = {} - - register(id: string, node: INode) { - this.registry[id] = node - } - - get(id: string) { - const node = this.registry[id] - if (node === undefined) { - console.error(`Tried to access schema "${id}, but that doesn't exit.`) - } - return node - } -} - -/** - * Registry for collections - */ -class CollectionRegistry implements Registry { - private registry: { [id: string]: string[] } = {} - - register(id: string, list: string[]) { - this.registry[id] = list - } - - get(id: string) { - const list = this.registry[id] - if (list === undefined) { - console.warn(`Tried to access collection "${id}", but that doesn't exist`) - } - return list ?? [] - } -} - -/** - * Registry for locales - */ -export interface Locale { - [key: string]: string -} - -class LocaleRegistry implements Registry { - private registry: { [id: string]: Locale } = {} - language: string = '' - - /** - * - * @param id locale identifier - * @param locale object mapping keys to translations - */ - register(id: string, locale: Locale): void { - this.registry[id] = locale - } - - get(id: string) { - const locale = this.registry[id] - if (locale === undefined) { - console.warn(`Tried to access locale "${id}", but that doesn't exist`) - } - return locale ?? [] - } - - getLocale(key: string) { - return this.get(this.language)[key] - } -} - -export const SCHEMAS = new SchemaRegistry() -export const COLLECTIONS = new CollectionRegistry() -export const LOCALES = new LocaleRegistry() - -/** - * Gets the locale of a key from the locale registry. - * - * @param key string or path that refers to a locale ID. - * If a string is given, an exact match is required. - * If a path is given, it finds the longest match at the end. - * @returns undefined if the key isn't found for the selected language - */ -export const locale = (key: string | Path) => { - if (typeof key === 'string') { - return LOCALES.getLocale(key) ?? key - } - let path = key.getArray().filter(e => (typeof e === 'string')) - while (path.length > 0) { - const locale = LOCALES.getLocale(path.join('.')) - if (locale !== undefined) return locale - path.shift() - } - return key.last() -} diff --git a/src/app/Sandbox.ts b/src/app/Sandbox.ts index 7364f0f1..3f391778 100644 --- a/src/app/Sandbox.ts +++ b/src/app/Sandbox.ts @@ -1,11 +1,14 @@ -import { ObjectNode } from '../nodes/ObjectNode'; -import { EnumNode } from '../nodes/EnumNode'; -import { NumberNode } from '../nodes/NumberNode'; -import { BooleanNode } from '../nodes/BooleanNode'; -import { RangeNode } from '../minecraft/nodes/RangeNode'; -import { MapNode } from '../nodes/MapNode'; -import { StringNode } from '../nodes/StringNode'; -import { ListNode } from '../nodes/ListNode'; +import { + ObjectNode, + EnumNode, + StringNode, + NumberNode, + BooleanNode, + RangeNode, + MapNode, + ListNode, + SCHEMAS +} from 'minecraft-schemas' const EntityCollection = ['sheep', 'pig'] diff --git a/src/app/app.ts b/src/app/app.ts index fd0c618c..788cecba 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -1,10 +1,13 @@ -import { DataModel } from '../model/DataModel' -import { TreeView } from '../view/TreeView' -import { SourceView } from '../view/SourceView' -import { ConditionSchema } from '../minecraft/schemas/Condition' -import { LootTableSchema } from '../minecraft/schemas/LootTable' +import { + DataModel, + TreeView, + SourceView, + ConditionSchema, + LootTableSchema, + LOCALES +} from 'minecraft-schemas' + import { SandboxSchema } from './Sandbox' -import { LOCALES } from '../Registries' const predicateModel = new DataModel(ConditionSchema) const lootTableModel = new DataModel(LootTableSchema) diff --git a/src/minecraft/nodes/RangeNode.ts b/src/minecraft/nodes/RangeNode.ts deleted file mode 100644 index f6aad652..00000000 --- a/src/minecraft/nodes/RangeNode.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { AbstractNode, NodeMods, RenderOptions, StateNode } from '../../nodes/AbstractNode' -import { Path } from '../../model/Path' -import { DataModel } from '../../model/DataModel' -import { TreeView } from '../../view/TreeView' -import { locale } from '../../Registries' - -export type IRange = number - | { min?: number, max?: number, type?: 'uniform' } - | { n?: number, p?: number, type: 'binomial' } - -export interface RangeNodeMods extends NodeMods { - integer?: boolean -} - -export class RangeNode extends AbstractNode implements StateNode { - integer: boolean - - constructor(mods?: RangeNodeMods) { - super(mods) - this.integer = mods?.integer ? mods.integer : false - } - - parseNumber(str: string): number { - return this.integer ? parseInt(str) : parseFloat(str) - } - - getState(el: Element): IRange { - const type = el.querySelector('select')!.value - if (type === 'exact') { - return this.parseNumber(el.querySelector('input')!.value) - } - if (type === 'range') { - const min = this.parseNumber(el.querySelectorAll('input')[0].value) - const max = this.parseNumber(el.querySelectorAll('input')[1].value) - return { - min: isNaN(min) ? undefined : min, - max: isNaN(max) ? undefined : max - } - } - const n = parseInt(el.querySelectorAll('input')[0].value) - const p = parseFloat(el.querySelectorAll('input')[1].value) - return { - type: 'binomial', - n: isNaN(n) ? undefined : n, - p: isNaN(p) ? undefined : p - } - } - - updateModel(el: Element, path: Path, model: DataModel) { - model.set(path, this.getState(el)) - } - - renderRaw(path: Path, value: IRange, view: TreeView, options?: RenderOptions) { - let curType = '' - let input = '' - if (value === undefined || typeof value === 'number') { - curType = 'exact' - input = `` - } else if (value.type === 'binomial') { - curType = 'binomial' - input = ` - - - ` - } else { - curType = 'range' - input = ` - - - ` - } - const id = view.register(el => { - (el as HTMLInputElement).value = curType - el.addEventListener('change', evt => { - const target = (el as HTMLInputElement).value - const newValue = this.default(target === 'exact' ? undefined : - target === 'binomial' ? {type: 'binomial'} : {}) - view.model.set(path, newValue) - evt.stopPropagation() - }) - }) - return `${options?.hideLabel ? `` : ``} - - ${input}` - } - - getClassName() { - return 'range-node' - } - - static isExact(v?: IRange) { - return v === undefined || typeof v === 'number' - } - - static isRange(v?: IRange) { - return v !== undefined && typeof v !== 'number' && v.type !== 'binomial' - } - - static isBinomial(v?: IRange) { - return v !== undefined && typeof v !== 'number' && v.type === 'binomial' - } -} diff --git a/src/minecraft/nodes/ResourceNode.ts b/src/minecraft/nodes/ResourceNode.ts deleted file mode 100644 index 609b64b5..00000000 --- a/src/minecraft/nodes/ResourceNode.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { NodeMods, RenderOptions } from '../../nodes/AbstractNode' -import { EnumNode } from '../../nodes/EnumNode' -import { Path } from '../../model/Path' -import { TreeView, getId } from '../../view/TreeView' -import { locale } from '../../Registries' - -export interface ResourceNodeMods extends NodeMods { - additional?: boolean -} - -export class ResourceNode extends EnumNode { - additional: boolean - - constructor(options: string[], mods?: ResourceNodeMods) { - super(options, { - transform: (v) => { - if (v === undefined || v.length === 0) return undefined - return v.startsWith('minecraft:') ? v : 'minecraft:' + v - }, ...mods}) - this.additional = mods?.additional ?? false - } - - getState(el: Element) { - if (this.additional) { - return el.querySelector('input')!.value - } else { - return super.getState(el) - } - } - - renderRaw(path: Path, value: string, view: TreeView, options?: RenderOptions) { - if (this.additional) { - const id = `datalist-${getId()}` - return `${options?.hideLabel ? `` : ``} - - ${this.options.map(o => - `` - ).join('')}` - } else { - return super.renderRaw(path, value, view, options) - } - } - - getClassName() { - return 'enum-node' - } -} diff --git a/src/minecraft/schemas/Collections.ts b/src/minecraft/schemas/Collections.ts deleted file mode 100644 index 40534f75..00000000 --- a/src/minecraft/schemas/Collections.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { COLLECTIONS } from '../../Registries' - -COLLECTIONS.register('conditions', [ - 'alternative', - 'requirements', - 'inverted', - 'reference', - 'entity_properties', - 'block_state_property', - 'match_tool', - 'damage_source_properties', - 'location_check', - 'weather_check', - 'time_check', - 'entity_scores', - 'random_chance', - 'random_chance_with_looting', - 'table_bonus', - 'killed_by_player', - 'survives_explosion' -]) - -COLLECTIONS.register('loot-entries', [ - 'empty', - 'item', - 'tag', - 'loot_table', - 'alternatives', - 'sequence', - 'group', - 'dynamic' -]) - -COLLECTIONS.register('loot-functions', [ - 'set_count', - 'set_damage', - 'set_name', - 'set_lore', - 'set_nbt', - 'set_attributes', - 'set_contents', - 'enchant_randomly', - 'enchant_with_levels', - 'looting_enchant', - 'limit_count', - 'furnace_smelt', - 'explosion_decay', - 'fill_player_head', - 'copy_name', - 'copy_nbt', - 'copy_state', - 'apply_bonus', - 'exploration_map', - 'set_stew_effect' -]) - -COLLECTIONS.register('enchantments', [ - 'aqua_affinity', - 'bane_of_arthropods', - 'blast_protection', - 'channeling', - 'binding_curse', - 'vanishing_curse', - 'depth_strider', - 'efficiency', - 'feather_falling', - 'fire_aspect', - 'fire_protection', - 'flame', - 'fortune', - 'frost_walker', - 'impaling', - 'infinity', - 'knockback', - 'looting', - 'loyalty', - 'luck_of_the_sea', - 'lure', - 'mending', - 'multishot', - 'piercing', - 'power', - 'projectile_protection', - 'protection', - 'punch', - 'quick_charge', - 'respiration', - 'riptide', - 'sharpness', - 'silk_touch', - 'smite', - 'sweeping', - 'thorns', - 'unbreaking' -]) - -COLLECTIONS.register('biomes', [ - 'badlands', - 'badlands_plateau', - 'bamboo_jungle', - 'bamboo_jungle_hills', - 'beach', - 'birch_forest', - 'birch_forest_hills', - 'cold_ocean', - 'dark_forest', - 'dark_forest_hills', - 'deep_cold_ocean', - 'deep_frozen_ocean', - 'deep_lukewarm_ocean', - 'deep_ocean', - 'deep_warm_ocean', - 'desert', - 'desert_hills', - 'desert_lakes', - 'end_barrens', - 'end_highlands', - 'end_midlands', - 'eroded_badlands', - 'flower_forest', - 'forest', - 'frozen_ocean', - 'frozen_river', - 'giant_spruce_taiga', - 'giant_spruce_taiga_hills', - 'giant_tree_taiga', - 'giant_tree_taiga_hills', - 'gravelly_mountains', - 'ice_spikes', - 'jungle', - 'jungle_edge', - 'jungle_hills', - 'lukewarm_ocean', - 'modified_badlands_plateau', - 'modified_gravelly_mountains', - 'modified_jungle', - 'modified_jungle_edge', - 'modified_wooded_badlands_plateau', - 'mountain_edge', - 'mountains', - 'mushroom_field_shore', - 'mushroom_fields', - 'nether', - 'ocean', - 'plains', - 'river', - 'savanna', - 'savanna_plateau', - 'shattered_savanna', - 'shattered_savanna_plateau', - 'small_end_islands', - 'snowy_beach', - 'snowy_mountains', - 'snowy_taiga', - 'snowy_taiga_hills', - 'snowy_taiga_mountains', - 'snowy_tundra', - 'stone_shore', - 'sunflower_plains', - 'swamp', - 'swamp_hills', - 'taiga', - 'taiga_hills', - 'taiga_mountains', - 'tall_birch_forest', - 'tall_birch_hills', - 'the_end', - 'the_void', - 'warm_ocean', - 'wooded_badlands_plateau', - 'wooded_hills', - 'wooded_mountains' -]) - -COLLECTIONS.register('structures', [ - 'pillager_outpost', - 'mineshaft', - 'mansion', - 'jungle_pyramid', - 'desert_pyramid', - 'igloo', - 'shipwreck', - 'swamp_hut', - 'stronghold', - 'monument', - 'ocean_ruin', - 'fortress', - 'endcity', - 'buried_treasure', - 'village' -]) - -COLLECTIONS.register('dimensions', [ - 'overworld', - 'the_nether', - 'the_end' -]) - -COLLECTIONS.register('slots', [ - 'mainhand', - 'offhand', - 'head', - 'chest', - 'legs', - 'feet' -]) - -COLLECTIONS.register('status-effects', [ - 'speed', - 'slowness', - 'haste', - 'mining_fatigue', - 'strength', - 'instant_health', - 'instant_damage', - 'jump_boost', - 'nausea', - 'regeneration', - 'resistance', - 'fire_resistance', - 'water_breathing', - 'invisibility', - 'blindness', - 'night_vision', - 'hunger', - 'weakness', - 'poison', - 'wither', - 'health_boost', - 'absorption', - 'saturation', - 'glowing', - 'levitation', - 'luck', - 'unluck', - 'slow_falling', - 'conduit_power', - 'dolphins_grace', - 'bad_omen', - 'hero_of_the_village' -]) - -COLLECTIONS.register('gamemodes', [ - 'survival', - 'creative', - 'adventure', - 'spectator' -]) - -COLLECTIONS.register('statistic-types', [ - 'minecraft:broken', - 'minecraft:crafted', - 'minecraft:custom', - 'minecraft:dropped', - 'minecraft:killed', - 'minecraft:killed_by', - 'minecraft:mined', - 'minecraft:picked_up', - 'minecraft:used', - 'killedByTeam', - 'teamkill' -]) - -COLLECTIONS.register('entity-sources', [ - 'this', - 'killer', - 'killer_player' -]) - -COLLECTIONS.register('copy-sources', [ - 'block_entity', - 'this', - 'killer', - 'killer_player' -]) - -COLLECTIONS.register('attributes', [ - 'generic.max_health', - 'generic.follow_range', - 'generic.knockback_resistance', - 'generic.movement_speed', - 'generic.attack_damage', - 'generic.armor', - 'generic.armor_toughness', - 'generic.attack_speed', - 'generic.luck', - 'horse.jump_strength', - 'generic.attack_knockback', - 'generic.flying_speed', - 'zombie.spawn_reinforcements' -]) - -COLLECTIONS.register('map-decorations', [ - 'mansion', - 'monument', - 'player', - 'frame', - 'red_marker', - 'blue_marker', - 'target_x', - 'target_point', - 'player_off_map', - 'player_off_limits', - 'red_x', - 'banner_white', - 'banner_orange', - 'banner_magenta', - 'banner_light_blue', - 'banner_yellow', - 'banner_lime', - 'banner_pink', - 'banner_gray', - 'banner_light_gray', - 'banner_cyan', - 'banner_purple', - 'banner_blue', - 'banner_brown', - 'banner_green', - 'banner_red', - 'banner_black' -]) diff --git a/src/minecraft/schemas/Condition.ts b/src/minecraft/schemas/Condition.ts deleted file mode 100644 index caed419a..00000000 --- a/src/minecraft/schemas/Condition.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { EnumNode } from '../../nodes/EnumNode'; -import { ResourceNode } from '../nodes/ResourceNode'; -import { NumberNode } from '../../nodes/NumberNode'; -import { BooleanNode } from '../../nodes/BooleanNode'; -import { ObjectNode, Switch, Case } from '../../nodes/ObjectNode'; -import { ListNode } from '../../nodes/ListNode'; -import { RangeNode } from '../nodes/RangeNode'; -import { MapNode } from '../../nodes/MapNode'; -import { StringNode } from '../../nodes/StringNode'; -import { ReferenceNode } from '../../nodes/ReferenceNode'; -import { SCHEMAS, COLLECTIONS } from '../../Registries'; - -import './Predicates' - -SCHEMAS.register('condition', new ObjectNode({ - condition: new ResourceNode(COLLECTIONS.get('conditions'), {default: () => 'random_chance'}), - [Switch]: 'condition', - [Case]: { - 'alternative': { - terms: new ListNode( - new ReferenceNode('condition') - ) - }, - 'block_state_property': { - block: new ResourceNode(COLLECTIONS.get('blocks')), - properties: new MapNode( - new StringNode(), - new StringNode() - ) - }, - 'damage_source_properties': { - predicate: new ReferenceNode('damage-source-predicate') - }, - 'entity_properties': { - entity: new EnumNode(COLLECTIONS.get('entity-sources'), 'this'), - predicate: new ReferenceNode('entity-predicate') - }, - 'entity_scores': { - entity: new EnumNode(COLLECTIONS.get('entity-sources'), 'this'), - scores: new MapNode( - new StringNode(), - new RangeNode() - ) - }, - 'inverted': { - term: new ReferenceNode('condition') - }, - 'killed_by_player': { - inverse: new BooleanNode() - }, - 'location_check': { - offsetX: new NumberNode({integer: true}), - offsetY: new NumberNode({integer: true}), - offsetZ: new NumberNode({integer: true}), - predicate: new ReferenceNode('location-predicate') - }, - 'match_tool': { - predicate: new ReferenceNode('item-predicate') - }, - 'random_chance': { - chance: new NumberNode({min: 0, max: 1}) - }, - 'random_chance_with_looting': { - chance: new NumberNode({min: 0, max: 1}), - looting_multiplier: new NumberNode() - }, - 'requirements': { - terms: new ListNode( - new ReferenceNode('condition') - ), - }, - 'reference': { - name: new StringNode() - }, - 'table_bonus': { - enchantment: new ResourceNode(COLLECTIONS.get('enchantments')), - chances: new ListNode( - new NumberNode({min: 0, max: 1}) - ) - }, - 'time_check': { - value: new RangeNode(), - period: new NumberNode() - }, - 'weather_check': { - raining: new BooleanNode(), - thrundering: new BooleanNode() - } - } -}, { - default: () => ({ - condition: 'random_chance', - chance: 0.5 - }) -})) - -export const ConditionSchema = SCHEMAS.get('condition') diff --git a/src/minecraft/schemas/LootTable.ts b/src/minecraft/schemas/LootTable.ts deleted file mode 100644 index 893c6a4f..00000000 --- a/src/minecraft/schemas/LootTable.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { EnumNode } from '../../nodes/EnumNode'; -import { ResourceNode } from '../nodes/ResourceNode'; -import { NumberNode } from '../../nodes/NumberNode'; -import { BooleanNode } from '../../nodes/BooleanNode'; -import { ObjectNode, Switch, Case } from '../../nodes/ObjectNode'; -import { ListNode } from '../../nodes/ListNode'; -import { RangeNode } from '../nodes/RangeNode'; -import { MapNode } from '../../nodes/MapNode'; -import { StringNode } from '../../nodes/StringNode'; -import { ReferenceNode } from '../../nodes/ReferenceNode'; -import { SCHEMAS, COLLECTIONS } from '../../Registries'; - -import './Predicates' - -const conditions = { - conditions: new ListNode( - new ReferenceNode('condition') - ) -} - -const functionsAndConditions = { - functions: new ListNode( - new ReferenceNode('loot-function') - ), - ...conditions -} - -SCHEMAS.register('loot-table', new ObjectNode({ - pools: new ListNode( - new ObjectNode({ - rolls: new RangeNode(), - entries: new ListNode( - new ReferenceNode('loot-entry') - ) - }) - ), - ...functionsAndConditions -}, { - default: () => ({ - pools: [ - { - rolls: 1, - entries: [ - { - type: 'item', - name: 'minecraft:stone' - } - ] - } - ] - }) -})) - -SCHEMAS.register('loot-entry', new ObjectNode({ - type: new EnumNode(COLLECTIONS.get('loot-entries'), {default: () => 'item'}), - weight: new NumberNode({ - integer: true, - min: 1, - enable: path => path.pop().get()?.length > 1 - && !['alternatives', 'group', 'sequence'].includes(path.push('type').get()) - }), - [Switch]: 'type', - [Case]: { - 'alternatives': { - children: new ListNode( - new ReferenceNode('loot-entry') - ), - ...functionsAndConditions - }, - 'dynamic': { - name: new StringNode(), - ...functionsAndConditions - }, - 'group': { - children: new ListNode( - new ReferenceNode('loot-entry') - ), - ...functionsAndConditions - }, - 'item': { - name: new StringNode(), - ...functionsAndConditions - }, - 'loot_table': { - name: new StringNode(), - ...functionsAndConditions - }, - 'sequence': { - children: new ListNode( - new ReferenceNode('loot-entry') - ), - ...functionsAndConditions - }, - 'tag': { - name: new StringNode(), - expand: new BooleanNode(), - ...functionsAndConditions - } - } -})) - -SCHEMAS.register('loot-function', new ObjectNode({ - function: new EnumNode(COLLECTIONS.get('loot-functions'), {default: () => 'set_count'}), - [Switch]: 'function', - [Case]: { - 'apply_bonus': { - enchantment: new EnumNode(COLLECTIONS.get('enchantments')), - formula: new EnumNode([ - 'uniform_bonus_count', - 'binomial_with_bonus_count', - 'ore_drops' - ]), - parameters: new ObjectNode({ - bonusMultiplier: new NumberNode({ - enable: path => path.pop().push('formula').get() === 'uniform_bonus_count' - }), - extra: new NumberNode({ - enable: path => path.pop().push('formula').get() === 'binomial_with_bonus_count' - }), - probability: new NumberNode({ - enable: path => path.pop().push('formula').get() === 'binomial_with_bonus_count' - }) - }, { - enable: path => path.push('formula').get() !== 'ore_drops' - }), - ...conditions - }, - 'copy_name': { - source: new EnumNode(COLLECTIONS.get('copy-sources')), - ...conditions - }, - 'copy_nbt': { - source: new EnumNode(COLLECTIONS.get('copy-sources')), - ops: new ListNode( - new ObjectNode({ - source: new StringNode(), - target: new StringNode(), - op: new EnumNode(['replace', 'append', 'merge']) - }) - ), - ...conditions - }, - 'copy_state': { - block: new ResourceNode(COLLECTIONS.get('blocks')), - properties: new ListNode( - new StringNode() - ), - ...conditions - }, - 'enchant_randomly': { - enchantments: new ListNode( - new EnumNode(COLLECTIONS.get('enchantments')) - ), - ...conditions - }, - 'enchant_with_levels': { - levels: new RangeNode(), - treasure: new BooleanNode(), - ...conditions - }, - 'exploration_map': { - destination: new EnumNode(COLLECTIONS.get('structures')), - decoration: new EnumNode(COLLECTIONS.get('map-decorations')), - zoom: new NumberNode({integer: true}), - search_radius: new NumberNode({integer: true}), - skip_existing_chunks: new BooleanNode(), - ...conditions - }, - 'fill_player_head': { - entity: new EnumNode(COLLECTIONS.get('entity-sources')), - ...conditions - }, - 'limit_count': { - limit: new RangeNode(), - ...conditions - }, - 'looting_enchant': { - count: new RangeNode(), - limit: new NumberNode({integer: true}), - ...conditions - }, - 'set_attributes': { - modifiers: new ListNode( - new ReferenceNode('attribute-modifier') - ), - ...conditions - }, - 'set_contents': { - entries: new ListNode( - new ReferenceNode('loot-entry') - ), - ...conditions - }, - 'set_count': { - count: new RangeNode(), - ...conditions - }, - 'set_damage': { - damage: new RangeNode(), - ...conditions - }, - 'set_lore': { - entity: new EnumNode(COLLECTIONS.get('entity-sources')), - lore: new ListNode( - new StringNode() - ), - replace: new BooleanNode(), - ...conditions - }, - 'set_name': { - entity: new EnumNode(COLLECTIONS.get('entity-sources')), - name: new StringNode(), - ...conditions - }, - 'set_nbt': { - tag: new StringNode(), - ...conditions - }, - 'set_stew_effect': { - effects: new ListNode( - new ReferenceNode('potion-effect') - ), - ...conditions - } - } -}, { - default: () => ({ - function: 'set_count', - count: 1 - }) -})) - -SCHEMAS.register('attribute-modifier', new ObjectNode({ - attribute: new EnumNode(COLLECTIONS.get('attributes')), - name: new StringNode(), - amount: new RangeNode(), - operation: new EnumNode([ - 'addition', - 'multiply_base', - 'multiply_total' - ]), - slot: new ListNode( - new EnumNode(COLLECTIONS.get('slots')) - ) -})) - -export const LootTableSchema = SCHEMAS.get('loot-table') diff --git a/src/minecraft/schemas/Predicates.ts b/src/minecraft/schemas/Predicates.ts deleted file mode 100644 index faf42b1f..00000000 --- a/src/minecraft/schemas/Predicates.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { ObjectNode } from '../../nodes/ObjectNode'; -import { ResourceNode } from '../nodes/ResourceNode'; -import { EnumNode } from '../../nodes/EnumNode'; -import { ListNode } from '../../nodes/ListNode'; -import { RangeNode } from '../nodes/RangeNode'; -import { StringNode } from '../../nodes/StringNode'; -import { ReferenceNode } from '../../nodes/ReferenceNode'; -import { BooleanNode } from '../../nodes/BooleanNode'; -import { MapNode } from '../../nodes/MapNode'; -import { SCHEMAS, COLLECTIONS } from '../../Registries'; - -import './Collections' - -SCHEMAS.register('item-predicate', new ObjectNode({ - item: new ResourceNode(COLLECTIONS.get('items')), - tag: new StringNode(), - count: new RangeNode(), - durability: new RangeNode(), - potion: new StringNode(), - nbt: new StringNode(), - enchantments: new ListNode( - new ReferenceNode('enchantment-predicate') - ) -})) - -SCHEMAS.register('enchantment-predicate', new ObjectNode({ - enchantment: new ResourceNode(COLLECTIONS.get('enchantments')), - levels: new RangeNode() -})) - -SCHEMAS.register('block-predicate', new ObjectNode({ - block: new ResourceNode(COLLECTIONS.get('blocks')), - tag: new StringNode(), - nbt: new StringNode(), - state: new MapNode( - new StringNode(), - new StringNode() - ) -})) - -SCHEMAS.register('fluid-predicate', new ObjectNode({ - fluid: new ResourceNode(COLLECTIONS.get('fluids')), - tag: new StringNode(), - nbt: new StringNode(), - state: new MapNode( - new StringNode(), - new StringNode() - ) -})) - -SCHEMAS.register('location-predicate', new ObjectNode({ - position: 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}), - light: new ObjectNode({ - light: new RangeNode() - }), - smokey: new BooleanNode(), - block: new ReferenceNode('block-predicate', {collapse: true}), - fluid: new ReferenceNode('fluid-predicate', {collapse: true}) -})) - -SCHEMAS.register('statistic-predicate', new ObjectNode({ - type: new EnumNode(COLLECTIONS.get('statistic-types')), - stat: new StringNode(), - value: new RangeNode() -})) - -SCHEMAS.register('player-predicate', new ObjectNode({ - gamemode: new EnumNode(COLLECTIONS.get('gamemodes')), - level: new RangeNode(), - advancements: new MapNode( - new StringNode(), - new BooleanNode() - ), - recipes: new MapNode( - new StringNode(), - new BooleanNode() - ), - stats: new ListNode( - new ReferenceNode('statistic-predicate') - ) -})) - -SCHEMAS.register('status-effect-predicate', new ObjectNode({ - amplifier: new RangeNode(), - duration: new RangeNode(), - ambient: new BooleanNode(), - visible: new BooleanNode() -})) - -SCHEMAS.register('distance-predicate', new ObjectNode({ - x: new RangeNode(), - y: new RangeNode(), - z: new RangeNode(), - absolute: new RangeNode(), - horizontal: new RangeNode() -})) - -SCHEMAS.register('entity-predicate', new ObjectNode({ - type: new StringNode(), - nbt: new StringNode(), - team: new StringNode(), - 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', {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() - }), - effects: new MapNode( - new ResourceNode(COLLECTIONS.get('status-effects')), - new ReferenceNode('status-effect-predicate') - ) -})) diff --git a/src/model/DataModel.ts b/src/model/DataModel.ts deleted file mode 100644 index 2eec6acd..00000000 --- a/src/model/DataModel.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Path } from "./Path" -import { INode } from "../nodes/AbstractNode" - -export interface ModelListener { - invalidated(model: DataModel): void -} - -/** - * Holding the data linked to a given schema - */ -export class DataModel { - data: any - schema: INode - /** A list of listeners that want to be notified when the model is invalidated */ - listeners: ModelListener[] - - /** - * @param schema node to use as schema for this model - */ - constructor(schema: INode) { - this.schema = schema - this.data = schema.default() - this.listeners = [] - } - - /** - * @param listener the listener to notify when the model is invalidated - */ - addListener(listener: ModelListener) { - this.listeners.push(listener) - } - - /** - * Force notify all listeners that the model is invalidated - */ - invalidate() { - this.listeners.forEach(listener => listener.invalidated(this)) - } - - /** - * Resets the full data and notifies listeners - * @param value new model data - */ - reset(value: any) { - this.data = value - this.invalidate() - } - - /** - * Gets the data at a specified path - * @param path path at which to find the data - * @returns undefined, if the the path does not exist in the data - */ - get(path: Path) { - let node = this.data; - for (let index of path) { - if (node === undefined) return node - node = node[index] - } - return node - } - - /** - * Updates the date on a path. Node will be removed when value is undefined - * @param path path to update - * @param value new data at the specified path - */ - set(path: Path, value: any) { - let node = this.data; - for (let index of path.pop()) { - if (node[index] === undefined) { - node[index] = {} - } - node = node[index] - } - - console.log('Set', path.toString(), JSON.stringify(value)) - - if (value === undefined || (typeof value === 'number' && isNaN(value))) { - if (typeof path.last() === 'number') { - node.splice(path.last(), 1) - } else { - delete node[path.last()] - } - } else { - node[path.last()] = value - } - - this.invalidate() - } -} diff --git a/src/model/Path.ts b/src/model/Path.ts deleted file mode 100644 index dbe00bda..00000000 --- a/src/model/Path.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { DataModel } from "./DataModel" - -export type PathElement = (string | number) - -/** - * Immutable helper class to represent a path in data - * @implements {Iterable} - */ -export class Path implements Iterable { - private arr: PathElement[] - model?: DataModel - - /** - * @param arr Initial array of path elements. Empty if not given - * @param model Model attached to this path - */ - constructor(arr?: PathElement[], model?: DataModel) { - this.arr = arr ?? [] - this.model = model - } - - /** - * The last element of this path - */ - last(): PathElement { - return this.arr[this.arr.length - 1] - } - - /** - * A new path with the last element removed - */ - pop(): Path { - return new Path(this.arr.slice(0, -1), this.model) - } - - /** - * A new path with an element added at the end - * @param element element to push at the end of the array - */ - push(element: PathElement): Path { - return new Path([...this.arr, element], this.model) - } - - copy(): Path { - return new Path([...this.arr], this.model) - } - - getArray(): PathElement[] { - return this.arr - } - - /** - * Attaches a model to this path and all paths created from this - * @param model - */ - withModel(model: DataModel): Path { - return new Path([...this.arr], model) - } - - /** - * Gets the data from the model if it was attached - * @returns undefined, if no model was attached - */ - get(): any { - return this.model?.get(this) - } - - toString(): string { - return `[${this.arr.map(e => e.toString()).join(', ')}]` - } - - *[Symbol.iterator]() { - for (const e of this.arr) { - yield e - } - } -} diff --git a/src/nodes/AbstractNode.ts b/src/nodes/AbstractNode.ts deleted file mode 100644 index 6e5aca79..00000000 --- a/src/nodes/AbstractNode.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { DataModel } from "../model/DataModel" -import { Path } from "../model/Path" -import { TreeView } from "../view/TreeView" - -/** - * Schema node that supports some standard transformations - */ -export interface INode { - default: (value?: T) => T | undefined - transform: (path: Path, value: T) => any - enabled: (path: Path, model: DataModel) => boolean - render: (path: Path, value: T, view: TreeView, options?: RenderOptions) => string - renderRaw: (path: Path, value: T, view: TreeView, options?: RenderOptions) => string -} - -export interface StateNode extends INode { - getState: (el: Element) => T -} - -export type RenderOptions = { - hideLabel?: boolean - syncModel?: boolean - collapse?: boolean -} - -export type NodeChildren = { - [name: string]: INode -} - -export type IDefault = (value?: T) => T | undefined -export type ITransform = (value: T) => any -export type IEnable = (path: Path) => boolean -export type IForce = () => boolean - -export interface NodeMods { - default?: IDefault - transform?: ITransform - enable?: IEnable - force?: IForce -} - -/** - * Basic implementation of the nodes - * - * h - */ -export abstract class AbstractNode implements INode { - defaultMod: IDefault - transformMod: ITransform - enableMod: IEnable - forceMod: IForce - - /** - * @param mods modifiers of the default transformations - */ - constructor(mods?: NodeMods) { - this.defaultMod = mods?.default ?? ((v) => v) - this.transformMod = mods?.transform ?? ((v) => v) - this.enableMod = mods?.enable ?? (() => true) - this.forceMod = mods?.force ?? (() => false) - } - - /** - * Runs when the element is mounted to the DOM - * @param el mounted element - * @param path data path that this node represents - * @param view corresponding tree view where this was mounted - */ - mounted(el: Element, path: Path, view: TreeView) { - el.addEventListener('change', evt => { - this.updateModel(el, path, view.model) - evt.stopPropagation() - }) - } - - /** - * Runs when the DOM element 'change' event is called - * @param el mounted element - * @param path data path that this node represents - * @param model corresponding model - */ - updateModel(el: Element, path: Path, model: DataModel) {} - - /** - * The default value of this node - * @param value optional original value - */ - default(value?: T) { - return this.defaultMod(value) - } - - /** - * Transforms the data model to the final output format - * @param - */ - transform(path: Path, value: T) { - if (!this.enabled(path)) return undefined - if (value === undefined && this.force()) value = this.default(value)! - return this.transformMod(value) - } - - /** - * Determines whether the node should be enabled for this path - * @param path - * @param model - */ - enabled(path: Path, model?: DataModel) { - if (model) path = path.withModel(model) - return this.enableMod(path.pop()) - } - - force(): boolean { - return this.forceMod() - } - - /** - * Wraps and renders the node - * @param path location - * @param value data used at - * @param view tree view context, containing the model - * @param options optional render options - * @returns string HTML wrapped representation of this node using the given data - */ - render(path: Path, value: T, view: TreeView, options?: RenderOptions): string { - if (!this.enabled(path, view.model)) return '' - - const id = view.register(el => { - this.mounted(el, path, view) - }) - return `
- ${this.renderRaw(path, value, view, options)} -
` - } - - /** - * Renders the node and handles events to update the model - * @param path - * @param value - * @param view tree view context, containing the model - * @param options optional render options - * @returns string HTML representation of this node using the given data - */ - abstract renderRaw(path: Path, value: T, view: TreeView, options?: RenderOptions): string - - /** - * The CSS classname used in the wrapped
- */ - abstract getClassName(): string -} diff --git a/src/nodes/BooleanNode.ts b/src/nodes/BooleanNode.ts deleted file mode 100644 index 8101a57d..00000000 --- a/src/nodes/BooleanNode.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { AbstractNode, NodeMods, RenderOptions } from "./AbstractNode"; -import { Path } from "../model/Path"; -import { TreeView } from "../view/TreeView"; -import { locale } from "../Registries"; - -/** - * Boolean node with two buttons for true/false - */ -export class BooleanNode extends AbstractNode { - - /** - * @param mods optional node modifiers - */ - constructor(mods?: NodeMods) { - super({ - default: () => false, - ...mods}) - } - - renderRaw(path: Path, value: boolean, view: TreeView, options?: RenderOptions) { - const falseButton = view.registerClick(el => { - view.model.set(path, !this.force() && value === false ? undefined : false) - }) - const trueButton = view.registerClick(el => { - view.model.set(path, !this.force() && value === true ? undefined : true) - }) - return `${options?.hideLabel ? `` : ``} - ${locale('false')} - ${locale('true')}` - } - - getClassName() { - return 'boolean-node' - } -} diff --git a/src/nodes/EnumNode.ts b/src/nodes/EnumNode.ts deleted file mode 100644 index e249bc8b..00000000 --- a/src/nodes/EnumNode.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AbstractNode, NodeMods, RenderOptions, StateNode } from './AbstractNode' -import { DataModel } from '../model/DataModel' -import { TreeView } from '../view/TreeView' -import { Path } from '../model/Path' -import { locale } from '../Registries' - -/** - * Enum node that shows a list of options to choose from - */ -export class EnumNode extends AbstractNode implements StateNode { - protected options: string[] - static className = 'enum-node' - - /** - * @param options options to choose from in the select - * @param mods optional node modifiers or a string to be the default value - */ - constructor(options: string[], mods?: NodeMods | string) { - super(typeof mods === 'string' ? { - default: () => mods, - force: () => true - } : mods) - this.options = options - } - - getState(el: Element) { - return el.querySelector('select')!.value - } - - updateModel(el: Element, path: Path, model: DataModel) { - model.set(path, this.getState(el)) - } - - renderRaw(path: Path, value: string, view: TreeView, options?: RenderOptions) { - const id = view.register(el => (el as HTMLInputElement).value = value) - return `${options?.hideLabel ? `` : ``} - ` - } - - getClassName() { - return 'enum-node' - } -} diff --git a/src/nodes/ListNode.ts b/src/nodes/ListNode.ts deleted file mode 100644 index a15916d7..00000000 --- a/src/nodes/ListNode.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { AbstractNode, NodeMods, INode } from './AbstractNode' -import { DataModel } from '../model/DataModel' -import { TreeView } from '../view/TreeView' -import { Path } from '../model/Path' -import { IObject } from './ObjectNode' -import { locale } from '../Registries' - -/** - * List node where children can be added and removed from - */ -export class ListNode extends AbstractNode { - protected children: INode - - /** - * @param values node used for its children - * @param mods optional node modifiers - */ - constructor(values: INode, mods?: NodeMods) { - super({ - default: () => [], - ...mods}) - this.children = values - } - - transform(path: Path, value: IObject[]) { - if (!(value instanceof Array)) return undefined - const res = value.map((obj, index) => - this.children.transform(path.push(index), obj) - ) - return this.transformMod(res) - } - - updateModel(el: Element, path: Path, model: DataModel) { - model.set(path, el.querySelector('select')?.value) - } - - renderRaw(path: Path, value: IObject[], view: TreeView) { - value = value || [] - const button = view.registerClick(el => { - view.model.set(path, [...value, this.children.default()]) - }) - return ` - -
- ${value.map((obj, index) => { - return this.renderEntry(path.push(index), obj, view) - }).join('')} -
` - } - - private renderEntry(path: Path, value: IObject, view: TreeView) { - const button = view.registerClick(el => { - view.model.set(path, undefined) - }) - return `
- ${this.children.render(path, value, view, {hideLabel: true})} -
` - } - - getClassName() { - return 'list-node' - } -} diff --git a/src/nodes/MapNode.ts b/src/nodes/MapNode.ts deleted file mode 100644 index 0ff9f645..00000000 --- a/src/nodes/MapNode.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { AbstractNode, NodeMods, INode, StateNode } from './AbstractNode' -import { TreeView } from '../view/TreeView' -import { Path } from '../model/Path' -import { IObject } from './ObjectNode' -import { locale } from '../Registries' - -export type IMap = { - [name: string]: IObject -} - -/** - * Map nodes similar to list nodes, but a string key is required to add children - */ -export class MapNode extends AbstractNode { - protected keys: StateNode - protected values: INode - - /** - * - * @param keys node used for the string key - * @param values node used for the map values - * @param mods optional node modifiers - */ - constructor(keys: StateNode, values: INode, mods?: NodeMods) { - super({ - default: () => ({}), - ...mods}) - this.keys = keys - this.values = values - } - - transform(path: Path, value: IMap) { - if (value === undefined) return undefined - let res: any = {} - Object.keys(value).forEach(f => - res[f] = this.values.transform(path.push(f), value[f]) - ) - return this.transformMod(res); - } - - renderRaw(path: Path, value: IMap, view: TreeView) { - value = value ?? [] - const button = view.registerClick(el => { - const key = this.keys.getState(el.parentElement!) - view.model.set(path.push(key), this.values.default()) - }) - return ` - ${this.keys.renderRaw(path, '', view, {hideLabel: true, syncModel: false})} - -
- ${Object.keys(value).map(key => { - return this.renderEntry(path.push(key), value[key], view) - }).join('')} -
` - } - - private renderEntry(path: Path, value: IObject, view: TreeView) { - const button = view.registerClick(el => { - view.model.set(path, undefined) - }) - return `
- ${this.values.render(path, value, view)} -
` - } - - getClassName() { - return 'map-node' - } -} diff --git a/src/nodes/NumberNode.ts b/src/nodes/NumberNode.ts deleted file mode 100644 index d4e3e804..00000000 --- a/src/nodes/NumberNode.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { AbstractNode, NodeMods, RenderOptions, StateNode } from './AbstractNode' -import { Path } from '../model/Path' -import { DataModel } from '../model/DataModel' -import { TreeView } from '../view/TreeView' -import { locale } from '../Registries' - -export interface NumberNodeMods extends NodeMods { - /** Whether numbers should be converted to integers on input */ - integer?: boolean - /** If specified, number will be capped at this minimum */ - min?: number - /** If specified, number will be capped at this maximum */ - max?: number -} - -/** - * Configurable number node with one text field - */ -export class NumberNode extends AbstractNode implements StateNode { - integer: boolean - min: number - max: number - - /** - * @param mods optional node modifiers - */ - constructor(mods?: NumberNodeMods) { - super({ - default: () => 0, - ...mods}) - this.integer = mods?.integer ?? false - this.min = mods?.min ?? -Infinity - this.max = mods?.max ?? Infinity - } - - getState(el: Element) { - const value = el.querySelector('input')!.value - const parsed = this.integer ? parseInt(value) : parseFloat(value) - if (parsed < this.min) return this.min - if (parsed > this.max) return this.max - return parsed - } - - updateModel(el: Element, path: Path, model: DataModel) { - model.set(path, this.getState(el)) - } - - renderRaw(path: Path, value: number, view: TreeView, options?: RenderOptions) { - return `${options?.hideLabel ? `` : ``} - ` - } - - getClassName() { - return 'number-node' - } -} diff --git a/src/nodes/ObjectNode.ts b/src/nodes/ObjectNode.ts deleted file mode 100644 index fe2d3191..00000000 --- a/src/nodes/ObjectNode.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { NodeMods, INode, NodeChildren, AbstractNode, RenderOptions } from './AbstractNode' -import { Path } from '../model/Path' -import { TreeView } from '../view/TreeView' -import { locale } from '../Registries' - -export const Switch = Symbol('switch') -export const Case = Symbol('case') - -export type NestedNodeChildren = { - [name: string]: NodeChildren -} - -export type IObject = { - [name: string]: any -} - -export type FilteredChildren = { - [name: string]: INode - /** The field to filter on */ - [Switch]?: string - /** Map of filter values to node fields */ - [Case]?: NestedNodeChildren -} - -export interface ObjectNodeMods extends NodeMods { - /** Whether the object can be collapsed. Necessary when recursively nesting. */ - collapse?: boolean -} - -/** - * Object node containing fields with different types. - * Has the ability to filter fields based on a switch field. - */ -export class ObjectNode extends AbstractNode { - fields: NodeChildren - cases: NestedNodeChildren - filter?: string - collapse?: boolean - - /** - * @param fields children containing the optional switch and case - * @param mods optional node modifiers - */ - 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 ?? {} - this.filter = _switch - } - - transform(path: Path, value: IObject) { - if (value === undefined) return undefined - const activeCase = this.filter ? this.cases[value[this.filter]] : {}; - const activeFields = {...this.fields, ...activeCase} - let res: any = {} - Object.keys(activeFields).forEach(f => { - return res[f] = activeFields[f].transform(path.push(f), value[f]) - }) - return this.transformMod(res); - } - - renderRaw(path: Path, value: IObject, view: TreeView, options?: RenderOptions) { - if (options?.hideLabel) { - return this.renderFields(path, value, view) - } else if (this.collapse || options?.collapse) { - if (value === undefined) { - const id = view.registerClick(() => view.model.set(path, this.default())) - return `` - } else { - const id = view.registerClick(() => view.model.set(path, undefined)) - return ` -
- ${this.renderFields(path, value, view)} -
` - } - } else { - return ` -
- ${this.renderFields(path, value, view)} -
` - } - } - - renderFields(path: Path, value: IObject, view: TreeView) { - value = value ?? {} - const activeCase = this.filter ? this.cases[value[this.filter]] : {}; - const activeFields = {...this.fields, ...activeCase} - return Object.keys(activeFields).map(f => { - return activeFields[f].render(path.push(f), value[f], view) - }).join('') - } - - getClassName() { - return 'object-node' - } -} diff --git a/src/nodes/ReferenceNode.ts b/src/nodes/ReferenceNode.ts deleted file mode 100644 index 3e4aadab..00000000 --- a/src/nodes/ReferenceNode.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { AbstractNode, NodeMods, RenderOptions, INode } from './AbstractNode' -import { TreeView } from '../view/TreeView' -import { Path } from '../model/Path' -import { SCHEMAS } from '../Registries' - -export interface AnyNodeMods extends NodeMods { - [name: string]: any -} - -/** - * Reference node. Must be used when recursively adding nodes. - */ -export class ReferenceNode extends AbstractNode { - protected reference: () => INode - options: RenderOptions - - /** - * @param id schema id that was registered - * @param mods optional node modifiers - */ - constructor(id: string, mods?: AnyNodeMods) { - super(mods) - this.options = { - collapse: mods?.collapse - } - this.reference = () => SCHEMAS.get(id) - } - - default(value?: any) { - return this.reference().default(value) - } - - transform(path: Path, value: any) { - return this.reference()?.transform(path, value) - } - - render(path: Path, value: any, view: TreeView, options?: RenderOptions) { - 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, {...this.options, ...options}) - } - - getClassName() { - return '' - } -} diff --git a/src/nodes/StringNode.ts b/src/nodes/StringNode.ts deleted file mode 100644 index 3dd0aa2d..00000000 --- a/src/nodes/StringNode.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { AbstractNode, NodeMods, RenderOptions, StateNode } from './AbstractNode' -import { Path } from '../model/Path' -import { DataModel } from '../model/DataModel' -import { TreeView } from '../view/TreeView' -import { locale } from '../Registries' - -export interface StringNodeMods extends NodeMods { - /** Whether the string can also be empty */ - allowEmpty?: boolean -} - -/** - * Simple string node with one text field - */ -export class StringNode extends AbstractNode implements StateNode { - allowEmpty: boolean - - /** - * @param mods optional node modifiers - */ - constructor(mods?: StringNodeMods) { - super(mods) - this.allowEmpty = mods?.allowEmpty ?? false - } - - getState(el: Element) { - return el.querySelector('input')!.value - } - - updateModel(el: Element, path: Path, model: DataModel) { - const value = this.getState(el) - model.set(path, this.allowEmpty || value !== '' ? value : undefined) - } - - renderRaw(path: Path, value: string, view: TreeView, options?: RenderOptions) { - return `${options?.hideLabel ? `` : ``} - ` - } - - getClassName() { - return 'string-node' - } -} diff --git a/src/view/SourceView.ts b/src/view/SourceView.ts deleted file mode 100644 index 1bea7618..00000000 --- a/src/view/SourceView.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { DataModel, ModelListener } from "../model/DataModel" -import { Path } from "../model/Path" - -type SourceViewOptions = { - indentation?: number | string, - rows?: number -} - -/** - * JSON representation view of the model. - * Renders the result in a